iT邦幫忙

2022 iThome 鐵人賽

DAY 4
0
Modern Web

身為 Node.js 開發者,可以知道一下的事系列 第 4

[Day 4] 用 Cluster 創建多個 Process

  • 分享至 

  • xImage
  •  

前面有提到 node 最大的優點就是他能夠善用單執行緒非同步的優點,同時處理多個 IO Request,但這同時也是他的缺點。單執行序意味著無論有多少顆 CPU,我們一個時間也只能在一個 CPU 上去執行我們的程式,當請求一多,這無疑是個負擔。

不過 node 提供了 cluster 這個 module 可以幫助我們解決這個問題!讓我們可以同時擁有單執行緒非同步的效能,又能善用多個 CPU。


Cluster Module

我們可以透過 node 本身提供的 node:cluster 這個 module 來建立 cluster。

Cluster 本身是一種從屬架構,當我們使用 node app.js,會產生主行程 (main-process),而每當 main process 執行 cluster.fork(); 則會產生子行程,並且每個 process 會有一個專屬的用來識別的 pid。

以官方程式碼為例,當我們用 node app.js 執行以下程式碼, cluster.isPrimary 屬性可以判斷目前執行的是不是 primary process。然後他使用了 for 迴圈,產生了與 cpu 數量相等的子行程,並設置事件監聽。

對於每個被產生的子行程,他們也會執行此段程式碼,不同的是他們會在 cluster.isPrimary 得到 false,並且啟用在 8000 port 上進行監聽的 http 服務。

源自官網文件

const cluster = require('node:cluster');
const http = require('node:http');
const numCPUs = require('node:os').cpus().length;
const process = require('node:process');

if (cluster.isPrimary) {
  console.log(`Primary ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

如何接收請求

以上面的範例來講,看似有許多 process 同時監聽 8000 port,一般來講這會造成衝突。
在 cluster 的機制中,8000 port 統一為 main process 進行監聽,當他收到請求後,會再透過主副行程之間進行溝通的 IPC 通道,將 http 請求轉發給子行程(默認為 round-robin 的排程方式來分配請求給子行程)。


process 之間的溝通

我們說過,cluster 的主副行程之間會使用 IPC channel 來進行溝通,那麼如何完成這件事呢?
我們只要分別在主副行程分別使用 work.on('message', callback)process.on('message', callback),並且使用 process.send(msg),便可以完成在主副行程間傳遞資料。

例如這個範例,當有請求進入 http://127.0.0.1:8000 ,便可以讓他們開始尬聊。

var cluster = require('cluster');
var http = require('http');
const numCPUs = require('node:os').cpus().length;
var worker;

if (cluster.isMaster) {
  for (var i = 0; i < numCPUs; i++) {
    worker = cluster.fork();
    worker.on('message', function(msg) {
      if (msg) {
        console.log(msg);
        worker.send(`${process.pid}: hello worker`);
      }
    });
  }
} else {
  process.on('message', function(msg) {
    if (msg) {
      console.log(msg)
    }
  });
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
    process.send(`${process.pid}: hello master`);
  }).listen(8000);
  console.log(`Worker ${process.pid} started`);
}

另外,除了 IPC 以外,通常 process 之間也會使用例如 Redis, Kafka or RabbitMQ,這些工具來達到資料共享或是彼此溝通的功能。


今天的文章就到這邊。
明天我們來認識一下另一個可以放我們輕鬆使 node 完成 multi-process 服務的另一種工具,PM2。


上一篇
[Day 3] Node 的核心機制 - Event Loop
下一篇
[Day 5] 用 PM2 無痛啟動 multi-process
系列文
身為 Node.js 開發者,可以知道一下的事9
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言